#pragma once

#include <utility>
#include <vector>
#include <memory>
#include <iostream>

enum class PipeType
{
    Vertical,
    Horizontal,
    NorthEast,
    NorthWest,
    SouthEast,
    SouthWest,
    Ground,
};

using Position = std::pair<int, int>;

class Pipe
{
public:
    Pipe(char token, PipeType type, const Position& pos) : 
        _token{token},
        _type{type},
        _position{pos} {}

    virtual std::vector<Position> nextPositions() const = 0;

    const Position position() const noexcept
    { return this->_position; }
    
    char token() const noexcept
    { return this->_token; }

    PipeType type() const noexcept
    { return this->_type; }

protected:
    char _token;
    PipeType _type;
    Position _position;
};

class VerticalPipe : public Pipe
{
public:
    VerticalPipe(const Position& pos) : 
        Pipe{'|', PipeType::Vertical, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class HorizontalPipe : public Pipe
{
public:
    HorizontalPipe(const Position& pos) : 
        Pipe{'-', PipeType::Horizontal, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class NorthEastPipe : public Pipe
{
public:
    NorthEastPipe(const Position& pos) : 
        Pipe{'L', PipeType::NorthEast, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class NorthWestPipe : public Pipe
{
public:
    NorthWestPipe(const Position& pos) : 
        Pipe{'J', PipeType::NorthWest, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class SouthEastPipe : public Pipe
{
public:
    SouthEastPipe(const Position& pos) : 
        Pipe{'F', PipeType::SouthEast, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class SouthWestPipe : public Pipe
{
public:
    SouthWestPipe(const Position& pos) : 
        Pipe{'7', PipeType::SouthWest, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class GroundPipe : public Pipe
{
public:
    GroundPipe(const Position& pos) : 
        Pipe{'.', PipeType::Ground, pos} {}
    virtual std::vector<Position> nextPositions() const override;
};

class PipeFactory
{
public:
    static std::shared_ptr<Pipe> create(char token, const Position& pos);
};